﻿using System;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Web;

namespace AcademicPlanner.Presentation
{
	/// <summary>
	/// FormSubmission encapsulates POST data and provides convenient ways to read it.
	/// POST data can be seen as partitioned using a common prefix to names.
	/// A <code>FormSubmission</code> object represents a single such partition.
	/// See <see cref="T:Form"/> for more on how form partitioning is designed.
	/// </summary>
	/// <seealso cref="T:Form"/>
	/// <version>1.0</version>
	/// <since>1.0</since>
	public class FormSubmission
	{
		private NameValueCollection values;

		private Dictionary<string, FormSubmission> subs = new Dictionary<string, FormSubmission>();


		/// <summary>
		/// Gets or sets the prefix of POST data partition names this FormSubmission represents.
		/// </summary>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public string NamesPrefix { get; set; }

		/// <summary>
		/// Gets the anti-forgery token sent along with the POST request (or an empty string);
		/// it is stored under the name "__signature".
		/// </summary>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public string Signature { get { return (values["__signature"] ?? ""); } }


		/// <summary>
		/// Sole constructor; sets the <c>NameValueCollection</c> containing the POST data.
		/// </summary>
		/// <param name="pValues">The <c>NameValueCollection</c> containing the POST data.</param>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public FormSubmission(NameValueCollection pValues)
		{
			values = pValues;
		}


		/// <summary>
		/// Creates and adds another <c>FormSubmission</c> under this FormSubmission.
		/// This method sets the <c>NamesPrefix</c> of the created <c>FormSubmission</c>
		/// to the complete name (returned by <see cref="M:Form.GetFieldName(string, string, string, int)"/>)
		/// of the given name under this FormSubmission.
		/// </summary>
		/// <param name="pName">The name of <c>FormSubmission</c> to create and add.</param>
		/// <returns>The <c>FormSubmission</c> created and added.</returns>
		/// <seealso cref="M:Form.GetFieldName(string, string, string, int)"/>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public FormSubmission AddSubFormSubmission(string pName)
		{
			var vSub = new FormSubmission(values);
			
			vSub.NamesPrefix = Form.GetFieldName(NamesPrefix, pName, null, null);
			subs[pName] = vSub;

			return vSub;
		}

		/// <summary>
		/// Gets a <c>FormSubmission</c> under this FormSubmission.
		/// </summary>
		/// <param name="pName">The name of <c>FormSubmission</c>.</param>
		/// <returns>The <c>FormSubmission</c> under this FormSubmission or <c>null</c>.</returns>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public FormSubmission GetSubFormSubmission(string pName)
		{
			return subs[pName];
		}

		/// <summary>
		/// This method is a convenience overload of <see cref="M:GetAsString(string, string)"/>
		/// with the second parameter <c>null</c>.
		/// </summary>
		/// <param name="pName">The field's name</param>
		/// <returns>The field's value as <c>string</c> or an empty <c>string</c>.</returns>
		/// <seealso cref="M:GetAsString(string, string)"/>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public string GetAsString(string pName)
		{
			return GetAsString(pName, null);
		}

		/// <summary>
		/// This method is a convenience overload of <see cref="M:GetAsString(string, string, int)"/>
		/// with the third parameter <c>null</c>.
		/// </summary>
		/// <param name="pName">The field's name</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <returns>The field's value as <c>string</c> or an empty <c>string</c>.</returns>
		/// <seealso cref="M:GetAsString(string, string, int)"/>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public string GetAsString(string pName, string pParent)
		{
			return GetAsString(pName, pParent, null);
		}

		/// <summary>
		/// Gets a POST data field value as a <c>string</c>. This method considers that the
		/// field's value is stored in the POST data collection keyed by the field's complete
		/// name as returned by <see cref="M:Form.GetFieldName(string, string, string, int)"/>
		/// using this FormSubmission's <c>NamesPrefix</c>.
		/// </summary>
		/// <param name="pName">The field's name</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <param name="pIndex">The field's index if it belongs to a collection; may be <c>null</c>.</param>
		/// <returns>The field's value as <c>string</c> or an empty <c>string</c>.</returns>
		/// <seealso cref="M:Form.GetFieldName(string, string, string, int)"/>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public string GetAsString(string pName, string pParent, int? pIndex)
		{
			string rVal = values[Form.GetFieldName(NamesPrefix, pName, pParent, pIndex)];
			return rVal ?? "";
		}

		/// <summary>
		/// This method is a convenience overload of <see cref="M:GetAsNumber(string, string)"/>
		/// with the second parameter <c>null</c>.
		/// </summary>
		/// <param name="pName">The field's name</param>
		/// <returns>The field's value as <c>double</c> or <see cref="P:System.Double.MinValue" />.</returns>
		/// <seealso cref="M:GetAsNumber(string, string)"/>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public double GetAsNumber(string pName)
		{
			return GetAsNumber(pName, null);
		}

		/// <summary>
		/// This method is a convenience overload of <see cref="M:GetAsNumber(string, string, int)"/>
		/// with the third parameter <c>null</c>.
		/// </summary>
		/// <param name="pName">The field's name</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <returns>The field's value as <c>double</c> or <see cref="P:System.Double.MinValue" />.</returns>
		/// <seealso cref="M:GetAsNumber(string, string, int)"/>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public double GetAsNumber(string pName, string pParent)
		{
			return GetAsNumber(pName, pParent, null);
		}

		/// <summary>
		/// Gets a POST data field value as a <c>double</c>. This method considers that the
		/// field's value is stored in the POST data collection keyed by the field's complete
		/// name as returned by <see cref="M:Form.GetFieldName(string, string, string, int)"/>
		/// using this FormSubmission's <c>NamesPrefix</c>.
		/// </summary>
		/// <param name="pName">The field's name</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <param name="pIndex">The field's index if it belongs to a collection; may be <c>null</c>.</param>
		/// <returns>The field's value as <c>double</c> or <see cref="P:System.Double.MinValue" />.</returns>
		/// <seealso cref="M:Form.GetFieldName(string, string, string, int)"/>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public double GetAsNumber(string pName, string pParent, int? pIndex)
		{
			double rVal = double.MinValue;
			string vVal = values[Form.GetFieldName(NamesPrefix, pName, pParent, pIndex)];

			if (vVal != null)
			{
				if (!double.TryParse(vVal, out rVal))
					rVal = double.MinValue;
			}

			return rVal; 
		}

		/// <summary>
		/// This method is a convenience overload of <see cref="M:GetAsInteger(string, string)"/>
		/// with the second parameter <c>null</c>.
		/// </summary>
		/// <param name="pName">The field's name</param>
		/// <returns>The field's value as <c>int</c> or <see cref="P:System.Int32.MinValue" />.</returns>
		/// <seealso cref="M:GetAsInteger(string, string)"/>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public int GetAsInteger(string pName)
		{
			return GetAsInteger(pName, null);
		}

		/// <summary>
		/// This method is a convenience overload of <see cref="M:GetAsInteger(string, string, int)"/>
		/// with the third parameter <c>null</c>.
		/// </summary>
		/// <param name="pName">The field's name</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <returns>The field's value as <c>int</c> or <see cref="P:System.Int32.MinValue" />.</returns>
		/// <seealso cref="M:GetAsInteger(string, string, int)"/>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public int GetAsInteger(string pName, string pParent)
		{
			return GetAsInteger(pName, pParent, null);
		}

		/// <summary>
		/// Gets a POST data field value as a <c>int</c>. This method considers that the
		/// field's value is stored in the POST data collection keyed by the field's complete
		/// name as returned by <see cref="M:Form.GetFieldName(string, string, string, int)"/>
		/// using this FormSubmission's <c>NamesPrefix</c>.
		/// </summary>
		/// <param name="pName">The field's name</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <param name="pIndex">The field's index if it belongs to a collection; may be <c>null</c>.</param>
		/// <returns>The field's value as <c>int</c> or <see cref="P:System.Int32.MinValue" />.</returns>
		/// <seealso cref="M:Form.GetFieldName(string, string, string, int)"/>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public int GetAsInteger(string pName, string pParent, int? pIndex)
		{
			int rVal = int.MinValue;
			string vVal = values[Form.GetFieldName(NamesPrefix, pName, pParent, pIndex)];

			if (vVal != null)
			{
				if (!int.TryParse(vVal, out rVal))
					rVal = int.MinValue;
			}

			return rVal; 
		}

		/// <summary>
		/// This method is a convenience overload of <see cref="M:GetAsBoolean(string, string)"/>
		/// with the second parameter <c>null</c>.
		/// </summary>
		/// <param name="pName">The field's name</param>
		/// <returns>The field's value as <c>bool</c> or <c>false</c>.</returns>
		/// <seealso cref="M:GetAsBoolean(string, string)"/>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public bool GetAsBoolean(string pName)
		{
			return GetAsBoolean(pName, null);
		}

		/// <summary>
		/// This method is a convenience overload of <see cref="M:GetAsBoolean(string, string, int)"/>
		/// with the third parameter <c>null</c>.
		/// </summary>
		/// <param name="pName">The field's name</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <returns>The field's value as <c>bool</c> or <c>false</c>.</returns>
		/// <seealso cref="M:GetAsBoolean(string, string, int)"/>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public bool GetAsBoolean(string pName, string pParent)
		{
			return GetAsBoolean(pName, pParent, null);
		}

		/// <summary>
		/// Gets a POST data field value as a <c>bool</c>. This method considers that the
		/// field's value is stored in the POST data collection keyed by the field's complete
		/// name as returned by <see cref="M:Form.GetFieldName(string, string, string, int)"/>
		/// using this FormSubmission's <c>NamesPrefix</c> and that it is an integer <c>0 or less</c>
		/// (for <c>false</c>) or <c>1 or more</c> for (<c>true</c>).
		/// </summary>
		/// <param name="pName">The field's name</param>
		/// <param name="pParent">The field's parent name; may be <c>null</c>.</param>
		/// <param name="pIndex">The field's index if it belongs to a collection; may be <c>null</c>.</param>
		/// <returns>The field's value as <c>bool</c> or <c>false</c>.</returns>
		/// <seealso cref="M:Form.GetFieldName(string, string, string, int)"/>
		/// <version>1.0</version>
		/// <since>1.0</since>
		public bool GetAsBoolean(string pName, string pParent, int? pIndex)
		{
			bool rVal = false;
			int vVal = GetAsInteger(pName, pParent, pIndex);

			if (vVal != int.MinValue && vVal > 0)
				rVal = true;

			return rVal;
		}

	}
}
